概述
本文来介绍一下 IntentService
,但从名字上看,应该是和 Service
有关,的确是这样,IntentService
是一种特殊的 Service
,它继承了 Service
并且是一个抽象类,因此必须是它的子类才能使用 IntentService
,IntentService
可用于执行后台耗时的任务,当任务执行完后它会自动停止,同时,由于 IntentService
是服务的原因,它的优先级比单纯的线程要高,因此可以用于执行一些高优先级的后台任务。
源码分析
在实现上,IntentService
封装了 HandlerThread
和 Handler
。
我们先来看一下它的构造函数:
1 | public IntentService(String name) { |
构造函数很简单,调用父类的构造函数,name 参数是赋给工作线程的名字。
我们再来看一下它的 onCreate()
方法:
1 | @Override |
onCreate()
方法会在 IntentService
第一次启动时被调用。
首先会构造一个 HandlerThread
,并且根据构造函数的 name 参数线程命名。
然后获取 HandlerThread
的 Looper
对象构造一个 Handler
对象。
再来看一下 onStartCommand
方法:
1 | public void setIntentRedelivery(boolean enabled) { |
每次启动 IntentService
,它的 onStartCommand
方法就会被调用一次,onStartCommand
调用 onStart
来处理 Intent
,然后根据 mRedelivery
变量来返回 START_REDELIVER_INTENT
或者 START_NOT_STICKY
,mRedelivery
变量是通过 setIntentRedelivery
方法来赋值的。
下面来温习一下 Service
中 onStartCommand
方法4种返回值所代表的意义:
- START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
- START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统将会把它置为started状态,系统不会自动重启该服务,直到startService(Intent intent)方法再次被调用。
- START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
- START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
onStart
方法对 Intent
的处理其实也就是通过 mServiceHandler
发送消息,然后在 Handler
中对消息进行处理。
1 | private final class ServiceHandler extends Handler { |
Handler
首先会在工作线程调用 onHandleIntent
来处理 Intent
,这个方法是我们在继承 IntentService
时需要实现的方法。
在 onHandleIntent
方法执行结束后,会调用 stopSelf(int startId)
来尝试停止服务。这里之所以调用这个方法而不是 stopSelf()
方法是因为 stopSelf()
会立刻停止服务,而这个时候消息队列中可能还有其它消息未处理完。stopSelf(int startId)
则会等待所有的消息都处理完才会终止服务。stopSelf(int startId)
方法会在尝试停止服务的时候判断最近启动服务的次数是否和 startId
相等,如果相等就立即停止,如果不相等则不停止服务。
因此,如果目前只存在一个后台任务,那么该任务一旦执行完,IntentService
就会停止,如果存在多个后台任务,那么当 onHandleIntent
执行完最后一个任务时才会停止服务。
如果 IntentService
停止后再次发送任务请求,那么就会创建新的 IntentService
对象来执行任务。
1 | @Override |
在 IntentService
销毁的时候会退出 Looper
循环。
使用方法
那么下面我们就带着上面分析源码的一些结论来验证一下:
因为 IntentService
是一个 Service
,那么就不要忘记在 AndriodManifest 里面添加声明。
1 | public void clickTestIntentService1(View view) { |
正常执行一个任务
1 | Test: MyIntentService Constructor com.example.heqiang.testsomething.MainActivity$MyIntentService@5020dde |
第一个任务执行结束后启动第二个任务
1 | Test: MyIntentService Constructor com.example.heqiang.testsomething.MainActivity$MyIntentService@90cfdd5 |
和上面的分析一致,第一个任务执行完后启动第二个任务,会重新创建了一个 IntentService
对象。
第一个任务正在执行时启动第二个任务
1 | Test: MyIntentService Constructor com.example.heqiang.testsomething.MainActivity$MyIntentService@c1163b7 |
和上面的分析一致,启动第二个任务时,调用了 IntentService
的 IntentService
,两个任务执行完毕后,IntentService
退出。
第一个任务正在执行的时候停止服务
1 | Test: MyIntentService Constructor com.example.heqiang.testsomething.MainActivity$MyIntentService@90cfdd5 |
任务执行过程中停止服务,正在执行的任务还是可以继续执行完的。
第一个任务正在执行的时候停止服务,再启动第二个任务
1 | Test: MyIntentService Constructor com.example.heqiang.testsomething.MainActivity$MyIntentService@b2ca251 |
启动第二个任务时,会立即创建一个新的服务,两个任务在两个线程中运行,所以其执行完的先后顺序不确定。